package nci60.data.protein
import nci60.data.protein.SourceOrganism
import nci60.data.maxquant.Project
import nci60.administration.Params

/**
 * GRAILS <a href="http://grails.org/doc/latest/guide/theWebLayer.html#controllers" target="_blank">Controllers</a>
 * This controller handles all actions related to the Chromosome tab. <p />
 * This addresses showing the chromosomal overview, detailed view of one chromosome, and showing all proteins on a chromosome and download this list.
 *
 * @autor <a href="mailto:Florian.J.Auer@googlemail.com">Florian J. Auer</a>
*/
class ProteinChromosomeController {

    /**
     * Makes the Helper Service accessable
     * @see helper.HelperService
     */
    def helperService

    /**
     * redirects to the list action as default entry point of this controller
     */def index() {
        redirect(action:'list')
    }

    /**
     * Entry action for listing all chromosomes. <p />
     * This gives all necessary information for the chromosomal overview. <p />
     * With a click on one Chromosome, this one will be shown via the {@link #show} action. 
     */
    def list = {
        def organisms = SourceOrganism.findAll()
        def chroms = [:]
        def foundGenesInProjects = [:]
        def foundGenes = [:]
        def allGenes = [:]
        organisms.each(){o->
            def tmp = helperService.getParameters('chromosomes.organism'+o.id+'.order').chromosomes['organism'+o.id].order.sort{it.key}*.value as List
            chroms.put(o.name, tmp)
            def chrMapGene = [:]
            tmp.each(){chrom->
                chrMapGene.put(chrom, 0)
            }
            foundGenesInProjects.put(o.name, [:])
            foundGenes.put(o.name, chrMapGene)
            allGenes.put(o.name, [:])
        }

        //get number of all genes on each chromosome
        ProteinChromosome.executeQuery("select c.organism.name, c.chromosome, count(distinct g.name) from ProteinChromosome c, GeneName g where c.sourceIPI=g.protein and g.isMainName=true group by c.chromosome, c.organism").each(){line->
            allGenes[line[0]].put(line[1], line[2])
        }
        //get number of all identified genes on each chromosome
        ProteinChromosome.executeQuery("select count(distinct g.name), c.chromosome, c.organism.name from ProteinChromosome c, LeadingProtein i, GeneName g where i.protein=c.sourceIPI and g.protein=i.protein and g.isMainName=true group by c.chromosome, c.organism").each(){line->
            foundGenes[line[2]][line[1]]=line[0]
        }

        //get number of identified genes for each project on each chromosome
        ProteinChromosome.executeQuery("select count(distinct g.name), c.chromosome, c.organism.name, i.currentProject.id from ProteinChromosome c, LeadingProtein i, GeneName g where i.protein=c.sourceIPI and g.protein=i.protein and g.isMainName=true group by c.chromosome, c.organism, i.currentProject.id").each(){line->
            def count = line[0]
            def chrom = line[1]
            def organism = line[2]
            def proj = line[3]
            if(!foundGenesInProjects[organism].containsKey(chrom)){foundGenesInProjects[organism].put(chrom, [:])}
            foundGenesInProjects[organism][chrom].put(proj, count)
        }

        def projects = Project.findAll()

        return [Organisms:organisms, Chromosomes:chroms, FoundGenes:foundGenes, FoundProjects:foundGenesInProjects, Projects:projects, AllGenes:allGenes]
    }

    /**
     * Shows one specific chromosome in detailed view<p />
     * With a click on one Chromosome area, the proteins in that region will be shown as list via the {@link #proteinList} action.
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              id
     * </td><td>        name of the chromosome, like "1", "20" or "Y"
     * </td></tr>
     * <tr><td class="z">
     *              organism
     * </td><td>        organism the dataset is originated from
     * </td></tr>
     * </table>
     */
    def show = {
//        println params
        if(!params.id) params.id="1"
        if(!params.organism) params.organism="1"
        if(!params.fileType) params.fileType="html"
        int maxEndPoint = ProteinChromosome.executeQuery("select max(c.end) from ProteinChromosome c where c.chromosome='"+params.id+"' and c.organism="+params.organism)[0]
        def projects = Project.findAll()
        def result = [:]

//        println maxEndPoint
        def rangeWidth = helperService.getParameters('chromosomes.organism'+params.organism+'.rangeWidth').chromosomes['organism'+params.organism].rangeWidth.toInteger()

        (0..((maxEndPoint/rangeWidth).toInteger())).each(){index->
            def rangeStart = (index*rangeWidth)+1
            def resultProj = [:]
            resultProj.put('all',[:])
            resultProj.put('identified',[:])
            resultProj.put('index',index)
            resultProj.put('projects',[:])
            projects.each(){p->
                resultProj.projects.put(p.id.toInteger(), [:])
            }
            result.put(rangeStart, resultProj)
        }

        def allGenes = [:]
        def max = 0
        ProteinChromosome.executeQuery('select g.name, c.start, c.end, c.strand from ProteinChromosome c, GeneName g where c.sourceIPI=g.protein and c.chromosome=:chrom and g.isMainName=true', [chrom:params.id]).each{line->
            def gene = line[0]
            def start = line[1]
            def end = line[2]
            def strand = line[3]
            def relStart = (strand==1) ? start : end
            result[((relStart/rangeWidth).toInteger()*rangeWidth)+1].all.put(gene,0)
            max = (max>result[((relStart/rangeWidth).toInteger()*rangeWidth)+1].all.size())?max:result[((relStart/rangeWidth).toInteger()*rangeWidth)+1].all.size()
            allGenes.put(gene, 0)
        }

        def identifiedGenes = [all:[:], projects:[:]]
        ProteinChromosome.executeQuery("select g.name, c.start, c.end, c.strand, i.currentProject.id from ProteinChromosome c, GeneName g, LeadingProtein i where c.sourceIPI=g.protein and c.chromosome=:chrom and i.protein=c.sourceIPI and g.isMainName=true", [chrom:params.id]).each(){line->
            def gene = line[0]
            def start = line[1]
            def end = line[2]
            def strand = line[3]
            def proj = line[4].toInteger()
            def relStart = (strand==1) ? start : end
            result[((relStart/rangeWidth).toInteger()*rangeWidth)+1].identified.put(gene,0)
//            println 'Project: '+proj+'\t'+result[((relStart/rangeWidth).toInteger()*StaticProperties.chromosomeRangeWidth)+1].projects.keySet()
            result[((relStart/rangeWidth).toInteger()*rangeWidth)+1].projects[proj].put(gene,0)
            identifiedGenes.all.put(gene, 0)
            if(!identifiedGenes.projects.containsKey(proj)){identifiedGenes.projects.put(proj, [:])}
            identifiedGenes.projects[proj].put(gene, 0)
        }

        def genes = [all:allGenes.size(), identified:identifiedGenes.all.size(), projects:[:]]
        identifiedGenes.projects.each{proj,projData->
            genes.projects.put(proj, projData.size())
        }
//        genes.each{println it}

        def organism = SourceOrganism.findById(params.organism)

        def marked = null
        if((params.markedStart!=null)&&(params.markedEnd!=null)){
            marked = ['Start':params.markedStart, 'End':params.markedEnd, 'Label':(params.label!=null)?params.label:'']
        }
        
        return [Ranges:result, Organism:organism, Chromosome:params.id, Projects:projects, Max:max, RangeWidth:rangeWidth, ChromosomeEnd:maxEndPoint, Marked:marked, Genes:genes]
    }
 

    /**
     * Shows a list of proteins contained in the specified region <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              id
     * </td><td>        name of the chromosome, like "1", "20" or "Y"
     * </td></tr>
     * <tr><td class="z">
     *              organism
     * </td><td>        organism the dataset is originated from
     * </td></tr>
     * <tr><td class="z">
     *              max
     * </td><td>        maximal chromosome position
     * </td></tr>
     * <tr><td class="z">
     *              start
     * </td><td>        startposition on the chromosome
     * </td></tr>
     * <tr><td class="z">
     *              end
     * </td><td>        endposition on the chromosome
     * </td></tr>
     * </table>
     */
    def proteinList = {
//        println params
        def checkForStupidInput ={input->
            def result = 0
            if((input!=0)&&(input.isInteger())&&(input.toInteger()>=0)){
                result = input.toInteger()
            }
            return result
        }
        if(!params.id) params.id="1"
        if(!params.organism) params.organism="1"
        def max = checkForStupidInput(params.max)
        def start = checkForStupidInput(params.start)
        def end = checkForStupidInput(params.end)
        def prots = ProteinChromosome.executeQuery("select i.protein, c, i.currentProject from ProteinChromosome c, LeadingProtein i where c.chromosome='"+params.chromosome+"' and c.organism.id="+params.organism+" and i.protein.id=c.sourceIPI.id and ((c.start>="+start+" and c.start<"+end+" and c.strand=1) or (c.end>="+start+" and c.end<"+end+" and c.strand=-1))").sort{a,b -> (a[2].id <=> b[2].id) ?: (a[1].start <=> b[1].start) }
//        prots.each(){println it}
//        println 'Max: '+params.max
        render(template:"proteinList", model:[Result:prots,Organism:params.organism, Chromosome:params.chromosome, Start:start, End:end, Max:max, Ranges:params.ranges])
    }

    
    /**
     * Generates a list of proteins contained in the specified region for download<p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              id
     * </td><td>        name of the chromosome, like "1", "20" or "Y"
     * </td></tr>
     * <tr><td class="z">
     *              organism
     * </td><td>        organism the dataset is originated from
     * </td></tr>
     * <tr><td class="z">
     *              max
     * </td><td>        maximal chromosome position
     * </td></tr>
     * <tr><td class="z">
     *              start
     * </td><td>        startposition on the chromosome
     * </td></tr>
     * <tr><td class="z">
     *              end
     * </td><td>        endposition on the chromosome
     * </td></tr>
     * </table>
     */
    def download = {
//        println params
        def prots = ProteinChromosome.executeQuery("select i.protein, c, i.currentProject from ProteinChromosome c, LeadingProtein i where c.chromosome='"+params.chromosome+"' and c.organism.id="+params.organism+" and i.protein.id=c.sourceIPI.id and ((c.start>="+params.start+" and c.start<"+params.end+" and c.strand=1) or (c.end>="+params.start+" and c.end<"+params.end+" and c.strand=-1))").sort{a,b -> (a[2].id <=> b[2].id) ?: (a[1].start <=> b[1].start) }
        response.setContentType("text/csv")
        response.setHeader("Content-disposition", "filename=${'Chromosome_'+params.chromosome+'.csv'}")
        response.outputStream << 'Protein;Gene Names;Description;Start;End;Strand;Experiment'+'\n'
        prots.each(){
            def genes = []
            it[0].genes.each{g->genes.add(g.name)}
            response.outputStream <<  it[0].mainId+';'+helperService.mergeList(genes, ',')+';'+it[0].description+';'+it[1].start+';'+it[1].end+';'+it[1].strand+';'+it[2].name+'\n'
        }
        response.outputStream.flush()
        return response
    }

}
